<?php

//	include_once("cmsConfig.php");

	define("BLOB_CHUNK_SIZE", 64);
	
	function byteSize(&$bytes){//fucking hack work around the inability to simply ask php how big something is.
	
		$has_mbstring = extension_loaded('mbstring') ||@dl(PHP_SHLIB_PREFIX.'mbstring.'.PHP_SHLIB_SUFFIX);
		$has_mb_shadow = (int) ini_get('mbstring.func_overload');
		$size = 0;
		if ($has_mbstring && ($has_mb_shadow & 2) ) {
   			$size = mb_strlen(&$bytes,'latin1');
		} else { 
   			$size = strlen(&$bytes);
		}
		return $size;	
	}
	
	function &headersFromCacheWithKey($key) {
		$headerPath = CMS_BINARY_CACHE_PATH . "/" . $key . ".header";
		$headers= array();		
		if (file_exists( $headerPath )) {// dont really need to worry about this too much..

			$f = file( $headerPath );
			//once we have read in the file we need to loop over.. and split into pairs
			foreach($f as $header){
				$headerParts = explode(":",$header);
				$headers[trim($headerParts[0])] = trim($headerParts[1]);
			}
		}
		return $headers;
	}
	function &binaryDataFromCacheWithKey($key) {
		//this will look for the specified file in the cache.. and return it.
		$blobPath = binaryDataPathWithKey($key);
		if (file_exists( $blobPath )) {// dont really need to worry about this too much..
			$f = file_get_contents( $blobPath );
			return $f;
		}
	}
	
	function binaryDataPathWithKey($key){
		return $blobPath = CMS_BINARY_CACHE_PATH . "/" . $key . ".dat";
	}

	function keyInBinaryDataAndHeaderCacheExists($key) {
		$blobPath = CMS_BINARY_CACHE_PATH . "/" . $key .".dat";
		$headerPath = CMS_BINARY_CACHE_PATH . "/" . $key . ".header";
		return 
			CMS_BINARY_CACHE_ENABLE && 
			(file_exists($blobPath)) && //((time() - filemtime($blobPath)) < CMS_BINARY_CACHE_EXPIRE) &&
			(file_exists($headerPath));// && //((time() - filemtime($headerPath)) < CMS_BINARY_CACHE_EXPIRE);
	}
	
	function insertBinaryDataIntoCacheWithKey(&$data, $key) {
		$blobPath = CMS_BINARY_CACHE_PATH . "/" . $key .".dat";
		if ($fh = fopen($blobPath, "w")) {
			fwrite($fh, $data);
			fclose($fh);
		}
	}
	
	function insertHeaderDataIntoCacheWithKey(&$headers, $key) {
		$headerPath = CMS_BINARY_CACHE_PATH . "/" . $key . ".header";
		if ($fh = fopen($headerPath, "w")) {
			foreach ($headers as $header => $headerValue) {
				fwrite($fh, $header .": " . $headerValue . "\n");
			}
			fclose($fh);
		}
	}
	
	function sendDoNotCacheHeaders() {
		header("Expires: Mon, 26 Jul 1997 05:00:00 GMT",true); // Date in the pasts
		header("Etag: " . md5("slartybartfast" .  microtimeFloat() * microtimeFloat()),true); // Date in the pasts
		header("max-age: 1", true);
		header("s-max-age: 1", true);
		header("Pragma: public", true);
		header("Cache-Control: must-revalidate, post-check=0, pre-check=0", true);
		header("Cache-Control: private",false);		
	}
	
	function sendCacheHeaders($key=null) {
		header("Cache-Control: max-age=" . (60 * 60 * 24),true);
		header("max-age: " . (60 * 60 * 24), true);
		header("Expires: ". date("r",strtotime("23:59 tomorrow")),true);
		header("Last-Modified: ". date("r",strtotime("12am yesterday")),true);
		header("Etag:" . md5($key));
	}
	
	function blobWithDocumentCode( $documentCode, $options, $db ){
			
		if(!is_null($documentCode) && is_numeric($documentCode) && $documentCode > 0 ){
			$cmsDocument = new CmsDocument($db);
			$content = $cmsDocument->openDocument($documentCode);
			if ($content){
				// should we look in the blob cache??
				$fileId = extractNumberWithKeyFromArray('fileId',$options, null);
				$useBlobCache = extractBooleanWithKeyFromArray('useBlobCache', $options, true);
				$fileType = extractNumberWithKeyFromArray('fileType', $options, 1);	
				
				$scale = false;
				if ($fileType == 1)
					$scale = extractBooleanWithKeyFromArray('scale', $options, false);

				$width = null;
				$height = null;
				if ($scale) {//only need to pull these out if we are scaling
					$width = extractNumberWithKeyFromArray('width', $options, null);//we could scale to zero or null
					$height = extractNumberWithKeyFromArray('height', $options, null);					
				}
				
				//if we have no width and no height then we dont actually care if scale is set.				
				if (is_null($width) && is_null($height)){
					$scale = null;
				}
				
				//myPrintR($options);
				
				$elementId = extractNumberWithKeyFromArray('elementId', $options, null);
				$fileType = extractNumberWithKeyFromArray("fileType", $options, null);
				
				$key = ( $documentCode . "-" . $fileId . "-" .  $elementId . "-" . $fileType  . "-" .$scale ."-" .  $width ."-" . $height );				
				
				$responseHeaders = null;
				$data = null;
				$path = null;
				
				if ($useBlobCache && keyInBinaryDataAndHeaderCacheExists( $key )) {
				
					$responseHeaders = headersFromCacheWithKey( $key );
					$path =  binaryDataPathWithKey( $key );
					$responseHeaders["X-BLOB-CACHE"] = "HIT";			
					
				} else {

					//SQL.. HOW QUAINT!!!
					$sqlWhere = " FROM binarydata WHERE documentcode = '".$documentCode."' ";
					
					if(!is_null($fileId)) {
						$sqlWhere .=" AND id=" . $fileId;
					}
					if(!is_null($elementId)) {
						$sqlWhere .= " AND elementid=".$elementId;
					}
					if (!is_null($fileType)) {
						$db->executeQuery("SELECT id FROM moviedescriptor where elementid='".$elementId."'");
						if ($row = $db->nextRow()) {
							$sqlWhere .= " AND filetypecode=".$fileType;
						}
					}
					
					//first we need to work out the size of the object.. grraahh
					//$q = "SELECT octect_size(content) as actualsize, " . $sql;

					$db->executeQuery("SELECT  filesize, mimetype, filename, elementid" . $sqlWhere);
					$result = $db->nextObject();
					
					$d = null;
					$bs = 10 * 1024 * 1024;
					if ($result->filesize > $bs ) {
						$ts = $result->filesize;
						
						for($i = 1; $i < $result->filesize; $i+= $bs ){
						
							$blocks = ($ts - $i ) > $bs  ?  $bs : ($ts - $i);
							
							//total 10MB blocks + REMAINDER
							$q="SELECT SUBSTRING(content from " . $i  . " for " . $blocks . ") AS c " . $sqlWhere;
						
							$db->executeQuery($q);
							$r = &$db->nextObject();
							
							$d .= $r->c;
						}
						
						$result->content = &$d;
						//$d=null;
						
					} else {
						$db->executeQuery("SELECT content " . $sqlWhere);
						$r = $db->nextObject();
						$result->content = &$r->content;
					}
					
					

					if( $result ) {			
					
						$responseHeaders = array();

						$responseHeaders["Content-Type"] = $result->mimetype;
						$responseHeaders["Content-Transfer-Encoding"] = "binary"; 
						$responseHeaders["X-FILENAME"] = $result->filename;
						
						if(is_null($elementId)) {
							$elementId = $result->elementid;
						}						
						
						// this needs intelligence.. and will need to look up the resize rules if we have a different scale specified
						// so that the scaling can then be removed from 
						// the form code
						
						//we actually want this here just in case..
								
						
						if ($result->mimetype != "video/x-flv" && ($scale || ($fileType != 1 && $fileType != 0))
							  
						
						) {
			
							$resizeOptions = array();
							
							$imageDescriptor = getImageDescriptor($content->documenttypecode, $elementId, $fileType, $db);
							if( !is_null($imageDescriptor) && $imageDescriptor && is_array($imageDescriptor) && array_key_exists('additionalCommands',$imageDescriptor) && 0 < strlen($imageDescriptor['additionalCommands'])) {
								$imageDescriptor['additionalCommands'] = preProcessAdditionalCommandsForDocumentElementAndFileType(
									$content,$elementId,$fileType,$db
								);
							}
							
							if (!is_null($imageDescriptor)) {
								$resizeOptions = array_merge($resizeOptions, $imageDescriptor);
							}
														
							// if we are scaling from the url .. we take the width and height and throw in some sensible options.
							if($scale) {
								//all of the other options are picked up from the defaults.. ie: preseveAscpect.. not to scale up .. etc..
								$resizeOptions['width'] = $width;
								$resizeOptions['height'] = $height;
							}
							
							
							$data = processImageData($result->content, $resizeOptions);
							// if we come out out here .. we must fangle the mime to jpg
							$responseHeaders["Content-Type"] = "image/jpeg";//$result->mimetype;
						} else {
							$data = $result->content;
						}
						
						if (!is_null($data) && byteSize($data) > 0 ) {						
							$responseHeaders["Content-length"] = byteSize($data);						
							insertHeaderDataIntoCacheWithKey($responseHeaders, $key);
							insertBinaryDataIntoCacheWithKey($data, $key);
							$path = binaryDataPathWithKey($key);
							$responseHeaders["X-BLOB-CACHE"] = "MISS";//this needs to happen after the caching.
						}
					}	
				}
				
				if (!is_null($path) && !is_null($responseHeaders) && is_array($responseHeaders)) {
					$a = array (
						'path' => $path,//&$data,
						'headers'=>&$responseHeaders,
						'key'=>$key 
					);
					
					return $a;
				}				
			}
		}
		$null= null;//nothing to see here..//fuckingh hack
	//	echo "path " . $path . " key " . $key . "<br/>";
		return $null;		
	}
	
	function streamBlobDataWithHeaders($data, $headers) {
		if (is_array($headers)) {
			foreach ($headers as $header => $headerValue){
				header($header.": ". $headerValue, true);				
			}
		}
		//for cunking from memory
		$sz = byteSize($data);
		for($i=0; $i < $sz; $i= $i + BLOB_CHUNK_SIZE) {
			$s = ($i + BLOB_CHUNK_SIZE >= $sz) ? BLOB_CHUNK_SIZE - (($i+BLOB_CHUNK_SIZE) - $sz) : BLOB_CHUNK_SIZE;
			echo substr($data,$i,$s);
			flush();
		}			
	}
	
	function streamFileWithOutUsingLotsOfRam($path){
		if (file_exists($path)){
			
			$fp = fopen($path,"rb",false);
			
			
			while (!feof($fp)) {
				echo fread($fp, 1024);
				flush();
			}
			fclose($fp);
			die;
		} else {
			dieWithInternalServerError("Could not find file: " . $path);
		}
	}